﻿using System;
using Aeon.Emulator.Video;

namespace Aeon.Presentation
{
    /// <summary>
    /// Renders 8-bit mode X graphics to a bitmap.
    /// </summary>
    internal sealed class GraphicsPresenterX : Presenter
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the GraphicsPresenterX class.
        /// </summary>
        /// <param name="dest">Pointer to destination bitmap.</param>
        /// <param name="videoMode">VideoMode instance describing the video mode.</param>
        public GraphicsPresenterX(IntPtr dest, VideoMode videoMode)
            : base(dest, videoMode)
        {
            unsafe
            {
                planes = new byte*[4];
                byte* srcPtr = (byte*)videoMode.VideoRam.ToPointer();
                planes[0] = srcPtr + VideoMode.PlaneSize * 0;
                planes[1] = srcPtr + VideoMode.PlaneSize * 1;
                planes[2] = srcPtr + VideoMode.PlaneSize * 2;
                planes[3] = srcPtr + VideoMode.PlaneSize * 3;
            }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Updates the bitmap to match the current state of the video RAM.
        /// </summary>
        public override void Update()
        {
            int width = this.VideoMode.Width;
            int height = this.VideoMode.Height;
            uint[] palette = this.VideoMode.Palette;
            int startOffset = this.VideoMode.StartOffset;
            int stride = this.VideoMode.Stride;
            int lineCompare = this.VideoMode.LineCompare / 2;

            unsafe
            {
                uint* destPtr = (uint*)this.Destination.ToPointer();

                int max = Math.Min(height, lineCompare + 1);

                for(int y = 0; y < max; y++)
                {
                    for(int x = 0; x < width; x++)
                        destPtr[(y * width) + x] = palette[planes[x % 4][(y * stride + (x / 4) + startOffset) & ushort.MaxValue]];
                }

                if(max < height)
                {
                    for(int y = max + 1; y < height; y++)
                    {
                        for(int x = 0; x < width; x++)
                            destPtr[(y * width) + x] = palette[planes[x % 4][((y - max) * stride + (x / 4)) & ushort.MaxValue]];
                    }
                }
            }
        }
        #endregion

        #region Private Fields
        private readonly unsafe byte*[] planes;
        #endregion
    }
}
